#include "GGL.h"
#include <cmath>
#include <iostream>

void GG::initLib()
{
    srand(time(0));
}

int GG::random(int min, int max)
{
    return (rand() % (max - min)) + min;
}

std::string GG::intToStr(int i)
{
    std::ostringstream oss;
    oss << i;
    return oss.str();
}

void GG::repeatSprite(GGSprite *spr, sf::RenderWindow *win, int times)
{
    if(times <= 0)
        return;

    times--;
    int iXPos = spr->x();
    spr->draw(win);

    for(int i = 0; i < times; i++) {
        spr->move(spr->x() + spr->getWidth() + 5, spr->y());
        spr->draw(win);
    }

    spr->move(iXPos, spr->y());
}

float GG::frandom(int min, int max)
{
    return (float) random(min, max);
}

/* Sprite */

GGSprite::GGSprite(std::string f, int i, int j)
{
    imgCount = i;
    if(j <= 0)
        fps = -1.0f;
    else
        fps = (float) (30.0f / ((float) j));

    tickCounter = 0;
    cFrame = 0;

    if(imgCount < 1)
        return;

    tex.loadFromFile(f);
    spr.setTexture(tex);
    width = tex.getSize().x / imgCount;

    spr.setTextureRect(sf::IntRect(0, 0, width, tex.getSize().y));
}

void GGSprite::tick()
{
    if(imgCount <= 1 || fps < 0)
        return;

    if(tickCounter >= fps) {
        spr.setTextureRect(sf::IntRect(width * cFrame, 0, width, tex.getSize().y));

        if(cFrame == imgCount - 1)
            cFrame = 0;
        else
            cFrame++;


        tickCounter = 0;
    }

    tickCounter++;
}

int GGSprite::getHeight()
{
    return tex.getSize().y;
}

int GGSprite::getWidth()
{
    return width;
}

float GGSprite::x()
{
    return spr.getPosition().x;
}

float GGSprite::y()
{
    return spr.getPosition().y;
}

sf::Sprite *GGSprite::getRawSprite()
{
    return &spr;
}

void GGSprite::move(float x, float y)
{
    spr.setPosition(x, y);
}

void GGSprite::draw(sf::RenderWindow *w)
{
    if(imgCount > 0)
        w->draw(spr);
}

void GGSprite::setImageCount(int ic) {
    imgCount = ic;
    if(cFrame > ic - 1)
        cFrame = ic - 1;
}

void GGSprite::setFPS(int f) {
    if(f <= 0)
        fps = -1.0f;
    else
        fps = (float) (30.0f / ((float) f));
}

void GGSprite::setCurrentImage(int ci) {
    if(ci >= 0 || ci < imgCount)
        cFrame = (int) ci;

    spr.setTextureRect(sf::IntRect(width * cFrame, 0, width, tex.getSize().y));
}

int GGSprite::getCurrentImage() {
    return cFrame;
}

void GGSprite::posAdd(float x, float y)
{
    spr.setPosition(spr.getPosition().x + x, spr.getPosition().y + y);
}

/* Background */

GGBackground::GGBackground(int ww, int wh, bool col)
{
    winW = ww;
    winH = wh;
    bg.setSize(sf::Vector2f(ww, wh));
    setType(col);

    //tex.loadFromFile("bg0.png");
    spr.setTexture(tex);
}

void GGBackground::setType(bool t)
{
    isAColor = t;
    if(!isAColor)
        setColor(sf::Color(0, 0, 0));
}

void GGBackground::setColor(sf::Color col)
{
    bg.setFillColor(col);
}


void GGBackground::setImage(std::string str)
{
    if(!tex.loadFromFile(str))
        std::cout << "Erreur chargement !" << std::endl;

    divX = ceil((float) ((float) winW / (float) tex.getSize().x));
    divY = ceil((float) ((float) winH / (float) tex.getSize().y));
}

void GGBackground::draw(sf::RenderWindow *win)
{
    win->draw(bg);

    if(!isAColor) {
        for(int y = 0; y < divY; y++) {
            for(int x = 0; x < divX; x++) {
                spr.setPosition(x * tex.getSize().x, y * tex.getSize().y);
                win->draw(spr);
            }
        }
    }
}

/* Object */

void GGObject::draw(sf::RenderWindow *w)
{
    spr->draw(w);
}

void GGObject::move(int x, int y)
{
    spr->move(x, y);
}

GGSprite *GGObject::getSprite()
{
    return spr;
}

void GGObject::event(sf::Event e)
{
    //Resolve VTable problems
}

void GGObject::tick()
{
    spr->tick();
}

void GGObject::setObjectId(int id)
{
    objId = id;
}

int GGObject::getObjectId()
{
    return objId;
}

int GGObject::getObjectType()
{
    return objType;
}

int GGObject::getParent()
{
    return parent;
}

bool GGObject::destroyed()
{
    return !alive;
}

/* Objects : Physics */

AABB GGObject::getAABB()
{
    AABB ret;
    ret.x = spr->x();
    ret.y = spr->y();
    ret.w = spr->getWidth();
    ret.h = spr->getHeight();

    return ret;
}

void GGObject::setSolid(bool s)
{
    solid = s;
}

bool GGObject::getSolid()
{
    return solid;
}

bool GGObject::isCollidingWidth(AABB t)
{
    if(!solid)
        return false;

    AABB myBB = getAABB();
    bool cV = (t.x >= myBB.x + myBB.w || t.x + t.w <= myBB.x);
    bool cH = (t.y >= myBB.y + myBB.h || t.y + t.h <= myBB.y);

    if(cV)
        bo = Horizontal;
    else if(cH)
        bo = Vertical;

    return !(cV || cH);
}

void GGObject::readjustPosition(AABB t)
{
    AABB myBB = getAABB();
    t.x += t.w / 2;
    t.y += t.h / 2;

    myBB.x += myBB.w / 2;
    myBB.y += myBB.h / 2;

    if(myBB.x < t.x && myBB.y <= t.y - t.h / 2 && myBB.y >= t.y + t.h / 2)
        spr->move(t.x - t.w / 2, spr->y());
    else if(myBB.x > t.x && myBB.y <= t.y - t.h / 2 && myBB.y >= t.y + t.h / 2)
        spr->move(t.x + t.w / 2, spr->y());
    else if(myBB.y > t.y && myBB.x <= t.x - t.w / 2 && myBB.x >= t.x + t.w / 2)
        spr->move(spr->y(), t.y + t.h / 2);
    else if(myBB.y < t.y && myBB.x <= t.x - t.w / 2 && myBB.x >= t.x + t.w / 2)
        spr->move(spr->y(), t.y - t.h / 2);
}

void GGObject::setDirection(bool top, bool bottom, bool left, bool right, bool topleft, bool topright, bool bottomleft, bool bottomright)
{
    int dirCount = 0;
    float dirsX[8];
    float dirsY[8];

    if(top) {
        dirsX[dirCount] = 0;
        dirsY[dirCount] = -1;
        dirCount++;
    }
    if(bottom) {
        dirsX[dirCount] = 0;
        dirsY[dirCount] = 1;
        dirCount++;
    }
    if(left) {
        dirsX[dirCount] = -1;
        dirsY[dirCount] = 0;
        dirCount++;
    }
    if(right) {
        dirsX[dirCount] = 1;
        dirsY[dirCount] = 0;
        dirCount++;
    }
    if(topleft) {
        dirsX[dirCount] = -(GG::frandom(1, 11) / 10.0f);
        dirsY[dirCount] = -(GG::frandom(1, 11) / 10.0f);
        dirCount++;
    }
    if(topright) {
        dirsX[dirCount] = GG::frandom(1, 11) / 10.0f;
        dirsY[dirCount] = -(GG::frandom(1, 11) / 10.0f);
        dirCount++;
    }
    if(bottomleft) {
        dirsX[dirCount] = -(GG::frandom(1, 11) / 10.0f);
        dirsY[dirCount] = GG::frandom(1, 11) / 10.0f;
        dirCount++;
    }
    if(bottomright) {
        dirsX[dirCount] = GG::frandom(1, 11) / 10.0f;
        dirsY[dirCount] = GG::frandom(1, 11) / 10.0f;
        dirCount++;
    }

    if(dirCount == 0)
        return;

    int choice = GG::random(0, dirCount);
    speedX = dirsX[choice];
    speedY = dirsY[choice];
    readjustSpeed();

    stopped = false;
}

void GGObject::moveTick()
{
    if(stopped)
        return;

    if(freeMove) {
        speedX = GG::random(-1, 2);
        speedY = GG::random(-1, 2);
    }

    if(followTarget) {
        float vx = vec.x - spr->x();
        float vy = vec.y - spr->y();

        float toAtan = vy / vx;
        short cAngle = atan(toAtan) * 180 / M_PI;
        if(vx < 0)
            cAngle += 180;

        angle = cAngle;
        float nrm = sqrt((vx * vx) + (vy * vy));
        speedX = vx / nrm;
        speedY = vy / nrm;
    }

    spr->posAdd(speed * speedX, speed * speedY);
}

int GGObject::getSpeed()
{
    return dspeed;
}

int GGObject::getAngle()
{
    return angle;
}

void GGObject::setSpeed(int s)
{
    dspeed = s;
    speed = s;
    readjustSpeed();
}

void GGObject::setAngle(int a)
{
    angle = a;
}

void GGObject::stop()
{
    stopped = true;
}

void GGObject::unstop()
{
    stopped = false;
}

bool GGObject::getStopped()
{
    return stopped;
}

void GGObject::setFollowTarget(bool f)
{
    followTarget = f;
}

void GGObject::setTarget(sf::Vector2f v)
{
    vec = v;
}

bool GGObject::isFollowingTarget()
{
    return followTarget;
}

sf::Vector2f GGObject::getTarget()
{
    return vec;
}

void GGObject::setFreeMove(bool fm)
{
    freeMove = fm;
}

bool GGObject::getFreeMove()
{
    return freeMove;
}

void GGObject::bounce()
{
    bounce(bo);
}

void GGObject::bounce(GGOrientation o)
{
    if(o == Horizontal)
        speedY = -speedY;
    else
        speedX = -speedX;
}

void GGObject::readjustSpeed()
{
    double coeff = speed * sqrt(speedX * speedX + speedY * speedY);
    speed *= (dspeed / coeff);
}

bool GGObject::isOutsideRoom()
{
    return (spr->x() + spr->getWidth() < 0 || spr->x() > winW || spr->y() + spr->getHeight() < 0 || spr->y() > winH);
}

void GGObject::setXSpeed(float xs)
{
    speedX = xs;
    readjustSpeed();
}

void GGObject::setYSpeed(float ys)
{
    speedY = ys;
    readjustSpeed();
}

float GGObject::getXSpeed()
{
    return speedX;
}

float GGObject::getYSpeed()
{
    return speedY;
}

/* Sound */

GGSound::GGSound(std::string uri, bool rep, int vol, bool isSound)
{
    type = isSound;
    if(isSound) {
        sndBuff = new sf::SoundBuffer();
        sndBuff->loadFromFile(uri);

        snd = new sf::Sound();
        snd->setBuffer(*sndBuff);
        snd->setLoop(rep);
        snd->setVolume(vol);
    } else {
        mus = new sf::Music();
        mus->openFromFile(uri);
        mus->setLoop(rep);
        mus->setVolume(vol);
    }
}

bool GGSound::isPlaying()
{
    if(type)
        return (snd->getStatus() == sf::Sound::Playing);
    else
        return (mus->getStatus() == sf::Music::Playing);
}

void GGSound::play()
{
    if(type)
        snd->play();
    else
        mus->play();
}

void GGSound::pause()
{
    if(type)
        snd->pause();
    else
        mus->pause();
}

void GGSound::stop()
{
    if(type)
        snd->stop();
    else
        mus->stop();
}

sf::Sound *GGSound::getSound()
{
    return snd;
}

sf::Music *GGSound::getMusic()
{
    return mus;
}

bool GGSound::getType()
{
    return type;
}

/* Timer */

GGTimer::GGTimer(int ms)
{
    ticks = 0;
    stopped = true;
    setInterval(ms);
}

void GGTimer::start()
{
    stopped = false;
}

void GGTimer::pause()
{
    stopped = true;
}

void GGTimer::stop()
{
    stopped = true;
    ticks = 0;
}

bool GGTimer::isOn()
{
    return !stopped;
}

bool GGTimer::isEnded()
{
    return (ticks >= interval);
}

int GGTimer::getTime()
{
    return ticks * 30;
}

void GGTimer::setInterval(int ms)
{
    interval = ms / 33.3333333333;
}

void GGTimer::tick()
{
    if(stopped)
        return;

    if(ticks >= interval)
        ticks = 0;
    else
        ticks++;
}
